#pragma once

#include "util.hpp"
#include "room.hpp"
#include <mutex>
#include <condition_variable>
#include <list>
#include <thread>

namespace liang
{
    template <class T>
    class matchQueue
    {
    public:
        int size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.size();
        }

        bool empty()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _list.empty();
        }

        void wait()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _cond.wait(lock);
        }

        void push(const T &data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.push_back(data);
            _cond.notify_all();
        }

        bool pop(T &data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_list.empty())
                return false;
            data = _list.front();
            _list.pop_front();
            return true;
        }

        void remove(T &data)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _list.remove(data);
        }

    private:
        std::mutex _mutex;
        std::condition_variable _cond; // 主要是为了阻塞消费者
        std::list<T> _list;            // 不用queue,有删除中间元素的需要
    };

    class matcher
    {
    public:
        matcher(roomManager *rm, onlineManager *om, userTable *ut)
            : _rm(rm), _om(om), _ut(ut),
              _th_bronze(std::thread(&matcher::thBronzeEntry, this)),
              _th_silver(std::thread(&matcher::thSilverEntry, this)),
              _th_gold(std::thread(&matcher::thGoldEntry, this))
        {
            DEBUG("游戏匹配模块初始化完毕");
        }
        ~matcher()
        {
            DEBUG("游戏匹配模块销毁完毕");
        }

        bool add(uint64_t uid)
        {
            Json::Value user;
            bool ret = _ut->selectById(uid, user);
            if (!ret)
            {
                DEBUG("获取玩家[%lu]信息失败", uid);
                return false;
            }
            int score = user["score"].asInt();
            if (score < 2000)
            {
                _q_bronze.push(uid);
            }
            else if (score >= 2000 && score < 3000)
            {
                _q_silver.push(uid);
            }
            else
            {
                _q_gold.push(uid);
            }
            return true;
        }

        bool del(uint64_t uid)
        {
            Json::Value user;
            bool ret = _ut->selectById(uid, user);
            if (!ret)
            {
                DEBUG("获取玩家[%lu]信息失败", uid);
                return false;
            }
            int score = user["score"].asInt();
            if (score < 2000)
            {
                _q_bronze.remove(uid);
            }
            else if (score >= 2000 && score < 3000)
            {
                _q_silver.remove(uid);
            }
            else
            {
                _q_gold.remove(uid);
            }
            return true;
        }

    private:
        void handleMatch(matchQueue<uint64_t> &queue)
        {
            while (1)
            {
                // 判断队列人数是否大于2
                while (queue.size() < 2)
                {
                    queue.wait();
                }
                uint64_t uid1;
                uint64_t uid2;
                // 出队两个玩家
                bool ret = queue.pop(uid1);
                if (!ret)
                {
                    continue;
                }
                ret = queue.pop(uid2);
                if (!ret)
                {
                    this->add(uid1);
                    continue;
                }
                // 校验两个玩家是否在线
                websocket_server::connection_ptr conn1 = _om->getConnFromHall(uid1);
                if (conn1 == nullptr)
                {
                    this->add(uid2);
                    continue;
                }
                websocket_server::connection_ptr conn2 = _om->getConnFromHall(uid2);
                if (conn2 == nullptr)
                {
                    this->add(uid1);
                    continue;
                }
                // 为两个玩家创建房间
                room::ptr rp = _rm->createRoom(uid1, uid2);
                if (rp == nullptr)
                {
                    this->add(uid1);
                    this->add(uid2);
                    continue;
                }
                // 对两个玩家进行响应
                Json::Value resp;
                resp["optype"] = "match_success";
                resp["result"] = true;
                std::string body;
                JsonHelper::serialize(resp, body);
                conn1->send(body);
                conn2->send(body);
                DEBUG("匹配成功: 房间[%lu] 玩家[%lu,%lu]", rp->id(), uid1, uid2);
            }
        }
        void thBronzeEntry() { handleMatch(_q_bronze); }
        void thSilverEntry() { handleMatch(_q_silver); }
        void thGoldEntry() { handleMatch(_q_gold); }

    private:
        matchQueue<uint64_t> _q_bronze;
        matchQueue<uint64_t> _q_silver;
        matchQueue<uint64_t> _q_gold;

        std::thread _th_bronze;
        std::thread _th_silver;
        std::thread _th_gold;

        roomManager *_rm;
        onlineManager *_om;
        userTable *_ut;
    };
}